<html>
<!-- $LastChangedDate: 2009-11-07 22:56:33 -0500 (Sat, 07 Nov 2009) $ -->
<!-- Copyright (C) 2004,2009 Jim Brooks http://www.palomino3d.org -->
<head>
<title>Palomino - Shader Module</title>
<link rel='stylesheet' type='text/css' href='docs.css'>
<link rel='icon' type='image/png' href='images/favicon.png'/>
</head>
<body>

<!-- ----------------------------------------------------------------------- -->
<h1>Palomino - Shader Module</h1>
<p>
&copy;2004,2009&nbsp;&nbsp;Jim E. Brooks
&nbsp;&nbsp;<a href='http://www.palomino3d.org'>http://www.palomino3d.org</a>
</p>
<hr>
<ul>
  <li><a href='index.html'>Index</a></li>
  <li><a href='#Shaders'>Shaders</a></li>
  <li><a href='#Light'>Light classes</a></li>
  <li><a href='#Macros'>Macros/Preprocessing</a></li>
  <li><a href='#Per-Model Shaders'>Per-Model Shaders</a></li>
  <li><a href='#Modifying Models for Shaders'>Modifying Models for Shaders</a></li>
  <li><a href='#Hardware Light'>Hardware Light</a></li>
  <li><a href='#Brightness'>Brightness</a></li>
  <li><a href='#Light Position'>Light Position</a></li>
  <li><a href='#Problems'>Problems</a></li>
</ul>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Shaders'></a>
<h2>Shaders</h2>
<p><!--date-->[2009/01]</p>
<p>
Shader programs can be attached to any node
<font size='-1'>(this exposes an OSG capability which may differ from other scene-graphs)</font>.
<code>ShaderFactory</code> caches pre-compiled shader programs.
Shaders are identified by a name ("default", "metallic", etc).
By default, <a href='module_graph.html#SceneGraph'>SceneGraph</a>
places nodes under a generic shader.
Effects code can attach a shader to a different group node
and pass it to <code>SceneGraph::AttachBranchNode()</code>.
</p>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Light'></a>
<a name='Light Singleton'></a>
<a name='Light Classes'></a>
<h2>Light Singleton and Light Classes</h2>
<p><!--date-->[2009/05]</p>
<p>
Light sources are implemented by shaders except
one hardware light is enabled for special-case 3D models.
Therefore, the <code>Light</code> class serves as an interface
over the private implementation classes <code>ShaderLight</code> and <code>HwLight</code>.
<code>Light</code> is a method-forwarding class.
<code>Light</code> provides a traditional OpenGL interface for light settings.
<code>ShaderLight</code> hides the names of uniforms used as parameters to shaders.
</p>
<pre>
This diagram depicts forwarding of methods (it isn't a class hierarchy).

         +-----------+
         |   Light   |
         | interface |
         +---+---+---+
            /     \
           /       \
+-------------+  +-----------+  
| ShaderLight |  |  HwLight  |  
|   (impl)    |  |   (impl)  |  
+-------------+  +-----------+  

Light::SetPosition() +--> ShaderLight::SetPosition()
                     +--> HwLight::SetPosition()

Client usage:
GET_LIGHT().SetLightPosition( lightNum, pos );
GET_LIGHT().SetLightColor( lightNum, color );
GET_LIGHT().SetFogDensity( fogDensity, fogMode );
</pre>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Macros'></a>
<h2>Macros/Preprocessing</h2>
<p><!--date-->[2009/05]</p>
<p>
A very simple macro preprocessor is implemented.
Macro keywords are prefixed by <b>@</b>.
</p>
<h3>@include - load an include file</h3>
<p>
@include executes recursively to support nested include files.
</p>
<pre>
@include funcs.glsl
void main()
{
}
</pre>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Per-Model Shaders'></a>
<h2>Per-Model Shaders</h2>
<p><!--date-->[2009/05]</p>
<p>
Lua scripts load 3D models.
Optionally, Lua can pick a shader for a particular 3D model.
</p>
<pre>
    nodeSort = NodeSort:new( { shader="metallic" } )
    sim.SceneGraph:AttachObjectSpecial( object, nodeSort )
</pre>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Modifying Models for Shaders'></a>
<h2>Modifying Models for Shaders</h2>
<p><!--date-->[2009/05]</p>
<p>
A few 3D models will need to be modified to render correctly with the shaders.
Problems cases are transparent polygons such as canopy glass and exhaust flame.
</p>
<p>
Example of inserting a shader uniform:
</p>
<pre>
StateSet {
...
  Uniform {
    DataVariance STATIC
    name "uni_glass"
    bool 1
  }
...
}
</pre>
<p>
In some models, to insert a uniform, a new StateSet must be inserted into a Group:
</p>
<pre>
Group {
  DataVariance STATIC
  name "ExternalFlame"
  nodeMask 0xffffffff
  cullingActive TRUE
  num_children 3
-- insert --
  StateSet {
    Uniform {
      DataVariance STATIC
      name "uni_alphaBias"
      float -0.5
    }
  }
-- insert --
...
}
</pre>
<p>
When converting 3D models,
osgconv should be run with <code>export OSG_OPTIMIZER=OFF</code>.
This prevents conversion problems such as losing node names, distorted geometry, etc.
</p>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Hardware Light'></a>
<a name='HwLight'></a>
<h2>Hardware Light</h2>
<p><!--date-->[2009/05]</p>
<p>
A few 3D models aren't compatible with any shader.
Therefore, if ShaderName="hw", ShaderFactory will produce a group node
with the hardware light instead of a shader.
<code>HwLight</code> singleton defines the hardware light (simulates the sun).
</p>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Brightness'></a>
<h2>Brightness</h2>
<p><!--date-->[2009/05]</p>
<p>
Brightness is adjusted by:
</p>
<ul>
  <li>Changing the shader uniform <code>uni_brightness</code>.</li>
  <li>Changing the hw light's color.</li>
  <li>Changing the hw fog's color.</li>
</ul>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Light Position'></a>
<h2>Light Position</h2>
<p><!--date-->[2009/05]</p>
<p>
C++ public methods set the position of lights in terms of world space.
But shaders are written in terms of eye space.
So, the C++ side transforms every light's position from world space to eye space.
</p>
<p>
Specifically, a PreDrawView listener is called before drawing every
<a href='module_view.html#View'>View</a>
which assigns a shader uniform variable with the light's transformed position.
The root-level modelview matrix is obtained
from the current View object being rendered.
</p>
<h3>Shader Programming Notes</h3>
<p>
The following is a common mistake.
Transforming a light position from world space to eye space,
(as if it were a vertex) is incorrect,
because the modelview matrix was computed for a particular node (3D model).
The correct way is for the C++ side to transform the light position
using the root-level (View) modelview matrix.
</p>
<pre>
uniform vec4 uni_light0_Position;  // defined in world space
...
    const vec4 ecPosition4 = gl_ModelViewMatrix * gl_Vertex;
    const vec4 ecLightPos = gl_ModelViewMatrix * uni_light0_position;  // WRONG
</pre>

<!-- ----------------------------------------------------------------------- -->
<hr>
<a name='Problems'></a>
<h2>Problems</h2>
<p><!--date-->[2009/05]</p>
<p>
These 3D models weren't originally created for rendering by shaders.
A major problem is transparent polygons.
Some aircraft models specify cockpit glass in the alpha of the texture,
others in the alpha of the RGBA color with textures disabled.
Solution was to write different shaders for different models or inserting uniforms.
A few models have proven virtual incompatible with shaders.
These models are placed under a node with a hardware light instead of shaders.
</p>

<!-- ********************************* END ********************************* -->
<hr>
<p align='center'>
<font size='-2'>
<!-- hhmts start -->
Last modified: Sat Nov  7 14:57:23 CST 2009
<!-- hhmts end -->
</font>
</p>

</body>
</html>
